package com.sx.basic.service.impl;

import com.sx.basic.BasicEntity;
import com.sx.basic.IBasicService;
import com.sx.basic.pageable.PageRequest;
import com.sx.basic.ResponseEntity;
import com.sx.basic.dao.BasicRepository;
import com.sx.basic.pageable.Condition;
import com.sx.basic.service.BasicService;
import com.sx.redis.service.RedisService;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Predicate;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;

/**
 * @author AdinZ
 * @date 2020/11/10 17:35
 */
public abstract class BasicServiceImpl<Repository extends BasicRepository<T,ID>, T extends BasicEntity, ID extends Serializable>
        implements BasicService<T, ID>, IBasicService<T,ID> {
    @Autowired protected Repository repository;

    @Resource protected RedisService redisService;

    @Override
    public T get(ID id) {
        Optional<T> optional = this.repository.findById(id);
        if(optional.isPresent()) return optional.get();
        else return null;
    }

    @Override
    public void delete(ID... ids) {
        for(ID id:ids){
            this.repository.deleteById(id);
        }
    }

    @Override @Modifying @Transactional
    public void delete(Condition condition) {
        List<T> list = this.findList(condition);
        if (list != null && list.size() > 0)
            this.repository.deleteAll(list);
    }

    @Override
    public T update(T t) {
        return this.repository.save(t);
    }

    @Override
    public void update(List<T> list) {
        this.repository.saveAll(list);
    }

    @Override
    public boolean save(T t) {
        this.repository.save(t);
        return true;
    }

    @Override
    public void save(List<T> list) {
        this.repository.saveAll(list);
    }

    @Override
    public List<T> findList(Condition condition) {
     return this.repository.findAll(new Query<T>(condition).getSpecification(),condition.getSort());
    }

    @Override
    public List<T> listById(Collection<ID> collection) {
        return this.repository.findAllById(collection);
    }

    @Override
    public ResponseEntity<List<T>> findPage(int page, int size) {
        return this.findPage(PageRequest.of(page,size));
    }

    @Override
    public ResponseEntity<List<T>> findPage(PageRequest pageable) {
        Page<T> page = this.repository.findAll(pageable);
        return this.toPage(page);
    }

    @Override
    public ResponseEntity<List<T>> findPage(Condition condition, PageRequest pageable) {
        Page<T> page = this.repository.findAll(new Query<T>(condition).getSpecification(), pageable);
        return toPage(page);
    }

    @Data
    private class Query<T> extends BasicEntity {
        private Specification<T> specification;
        private Sort sort=Sort.unsorted();
        public Query(Condition condition){
            if(condition!=null&&!condition.isEmpty()){
                this.specification= (Specification<T>) (root, cq, cb) -> {
                    List<Predicate> list=new ArrayList<>();
                    condition.forEach((k,v)->{
                        switch (v.getKeyword()){
                            case LIKE:list.add(cb.like(root.get(k), (String) v.getValue())); break;
                            case NE:list.add(cb.notEqual(root.get(k),v.getValue())); break;
                            case LT:list.add(cb.lt(root.get(k), Long.parseLong(v.getValue()+""))); break;
                            case LE:list.add(cb.le(root.get(k),Long.parseLong(v.getValue()+"")));break;
                            case IN:
                                CriteriaBuilder.In<Object> in = cb.in(root.get(k));
                                in.value(v.getValue());
                                list.add(in);
                                break;
                            case GT:list.add(cb.gt(root.get(k),Long.parseLong(v.getValue()+"")));break;
                            case GE:list.add(cb.ge(root.get(k),Long.parseLong(v.getValue()+"")));break;
                            case EQ:list.add(cb.equal(root.get(k),v.getValue()));break;
                            case IS_NULL:list.add(cb.isNull(root.get(k)));break;
                            case IS_NOT_NULL:list.add(cb.isNotNull(root.get(k)));break;

                        }
                    });
                    return cb.and(list.toArray(new Predicate[list.size()]));
                };
            }
            if(condition!=null&&condition.getSort()!=null){
                this.sort=condition.getSort();
            }
        }
    }

    public ResponseEntity<List<T>> toPage(Page page){
        ResponseEntity response=new ResponseEntity().toSuccess(page.getContent());
        response.setTotal(page.getTotalElements());
        return response;
    }

}
